<template>
    <view class="color-selector">
        <view
            :class="[classes, { disabled: disabled }]"
            :style="[styles]"
            @click="!disabled && (show = true)"
        >
            <slot></slot>
        </view>
        <uxt-modal
            max-height="70%"
            max-width="90%"
            min-width="70%"
            v-model="show"
            :color="color"
            :bg-color="bgColor"
            :position="position"
            @open="sure = false"
            @close="!sure && $emit('cancel', { detail: { value: value1 } })"
            @opened="$emit('opened', { detail: { value: value1 } })"
            @closed="$emit('closed', { detail: { value: value1 } })"
        >
            <template slot="title">
                <view class="flex justify-between">
                    <view @click="show = false">取消</view>
                    <view v-if="clearable" @click="handleClear">清空</view>
                    <view @click="handleSure">确定</view>
                </view>
            </template>
            <view class="padding-lr padding-tb-xs flex flex-wrap solid-bottom">
                <view
                    :class="[{ 'solid-red': value11 === 'theme' }]"
                    class="bg-theme item padding-xs margin-xs"
                    @click="handleClick('theme')"
                >
                    主题
                </view>
            </view>
            <view class="padding-lr padding-tb-xs flex flex-wrap solid-bottom">
                <view
                    v-for="(color, index) in colors"
                    :key="index"
                    :class="[`bg-${color}`, { 'solid-red': value11 === color }]"
                    class="item padding-sm margin-xs"
                    @click="handleClick(color)"
                ></view>
            </view>
            <view v-if="light" class="padding-lr padding-tb-xs flex flex-wrap solid-bottom">
                <view
                    v-for="(color, index) in colors1"
                    :key="index"
                    :class="[`bg-light-${color}`, { 'solid-red': value11 === `light-${color}` }]"
                    class="item padding-sm margin-xs"
                    @click="handleClick(`light-${color}`)"
                ></view>
            </view>
            <view v-if="gradual" class="padding-lr padding-tb-xs flex flex-wrap solid-bottom">
                <view
                    v-for="(color, index) in colors1"
                    :key="index"
                    :class="[
                        `bg-gradual-${color}`,
                        { 'solid-red': value11 === `gradual-${color}` }
                    ]"
                    class="item padding-sm margin-xs"
                    @click="handleClick(`gradual-${color}`)"
                ></view>
            </view>
            <view v-if="customize" class="padding-lr-lg padding-tb-sm flex">
                <view
                    class="color-area"
                    @touchstart="handleChangeColorStart"
                    @touchmove.stop.prevent="handleChangeColor"
                    :style="{
                        width: `${areaSize}px`,
                        height: `${areaSize}px`,
                        backgroundColor: areaBgColor
                    }"
                >
                    <view
                        class="dot solids-theme round"
                        :style="{
                            width: `${hueWidth}px`,
                            height: `${hueWidth}px`,
                            top: `${dotTop}px`,
                            left: `${dotLeft}px`,
                            backgroundColor: value111
                        }"
                    ></view>
                </view>
                <view
                    class="hue-area margin-lr"
                    @touchstart="handleChangeHueStart"
                    @touchmove.stop.prevent="handleChangeHue"
                    :style="{
                        width: `${hueWidth}px`,
                        height: `${areaSize}px`
                    }"
                >
                    <view
                        class="btn bg-theme"
                        :style="{
                            left: '-3px',
                            top: `${btnTop}px`,
                            width: `${hueWidth + 6}px`,
                            height: `${hueWidth - 2}px`,
                            backgroundColor: areaBgColor
                        }"
                    ></view>
                </view>
                <view class="flex-sub flex flex-col justify-center align-center">
                    <view
                        class="preview solid margin-bottom-sm"
                        :style="{
                            backgroundColor: value111
                        }"
                    ></view>
                    <view>{{ value111 }}</view>
                </view>
            </view>
        </uxt-modal>
    </view>
</template>

<script>
import baseMixin from '../mixins/base.js'
import formMixin from '../mixins/form.js'
import uxtModal from './uxt-modal.vue'
import { hsv2rgb, rgb2hsv, rgb2Hex } from '../utils/hsl.js'

export default {
    name: 'uxt-color-selector',
    mixins: [baseMixin, formMixin],
    components: {
        uxtModal
    },
    props: {
        value: {
            default: ''
        },
        // 弹出位置 bottom/center
        position: {
            type: String,
            default: 'bottom',
            validator(value) {
                return ['bottom', 'center'].includes(value)
            }
        },
        // 是否显示清除
        clearable: {
            type: Boolean,
            default: false
        },
        // 是否显示系统定义的颜色
        system: {
            type: Boolean,
            default: true
        },
        // 是否显示系统定义的亮色
        light: {
            type: Boolean,
            default: false
        },
        // 是否显示系统定义的渐变色
        gradual: {
            type: Boolean,
            default: false
        },
        // 是否显示自定义颜色选择
        customize: {
            type: Boolean,
            default: true
        },
        bgColor: {
            default: '#e0e0e0'
        }
    },
    data() {
        return {
            show: false,
            value11: '',
            sure: false,
            colors1: [
                'red',
                'orange',
                'yellow',
                'olive',
                'green',
                'cyan',
                'blue',
                'purple',
                'mauve',
                'pink',
                'brown',
                'grey'
            ],
            colors2: ['gray', 'black', 'white'],
            isCustomize: false,
            areaSize: 150, // 颜色选择区长宽
            hueWidth: 10, // hue bar 宽
            dotTop: -5, // 圆点top
            dotLeft: -5, // 圆点left
            btnTop: -4, // 滑块top
            areaBgColor: 'rgb(255, 0, 0)', // 颜色选择区背景色，通过hue bar变化
            value111: '#FFFFFF', // 选择的颜色
            huePos: null, // hue bar 位置等信息
            areaPos: null, //color area 位置等信息
            rgb: {
                r: 255,
                g: 255,
                b: 255
            }
        }
    },
    computed: {
        bgClass() {
            return this.getColor(this.value, 'bg-').classes
        },
        bgStyle() {
            return this.getColor(this.value).styles
        },
        colors() {
            return [...this.colors1, ...this.colors2]
        }
    },
    watch: {
        show(newVal, oldVal) {
            if (newVal) {
                this.init()
            }
        }
    },
    methods: {
        init() {
            if (this.value.startsWith('#')) {
                this.isCustomize = true
                this.value11 = ''
                this.value111 = this.value1
            } else {
                this.isCustomize = false
                this.value11 = this.value1
                this.value111 = '#FFFFFF'
            }
        },
        handleClear() {
            this.show = false
            this.sure = true
            if (this.value1 === '') {
                this.$emit('clear', { detail: { value: this.value1 } })
                return
            }
            this.value1 = ''
            this.$emit('clear', { detail: { value: this.value1 } })
            this.$emit('change', { detail: { value: this.value1 } })
            this.$emit('input', this.value1)
        },
        handleClick(color) {
            this.value11 = color
            this.isCustomize = false
        },
        handleSure() {
            this.show = false
            this.sure = true
            let val = this.isCustomize ? this.value111 : this.value11
            if (val === this.getValue()) {
                return
            }
            this.value1 = val
            this.$emit('change', { detail: { value: this.value1 } })
            this.$emit('input', this.value1)
        },
        async handleChangeHueStart(e) {
            this.isCustomize = true
            this.value11 = ''
            this.huePos = await this.getElSize('.hue-area', this)
            this.handleChangeHue(e)
        },
        handleChangeHue(e) {
            if (
                e.changedTouches[0].clientY < this.huePos.top ||
                e.changedTouches[0].clientY > this.huePos.top + this.areaSize
            ) {
                return
            }
            this.btnTop = e.changedTouches[0].clientY - this.huePos.top - 4
            let h = ((this.btnTop + 4) / this.areaSize) * 360
            this.rgb = hsv2rgb(h, 1, 1)
            this.areaBgColor = `rgb(${this.rgb.r},${this.rgb.g},${this.rgb.b})`

            let s = (this.dotLeft + 5) / this.areaSize
            let l = (this.areaSize - this.dotTop + 5) / this.areaSize
            let hsv = rgb2hsv(this.rgb.r, this.rgb.g, this.rgb.b)
            let rgb = hsv2rgb(hsv.h, s, l)
            this.value111 = rgb2Hex(rgb.r, rgb.g, rgb.b).toUpperCase()
        },
        async handleChangeColorStart(e) {
            this.isCustomize = true
            this.value11 = ''
            this.areaPos = await this.getElSize('.color-area', this)
            this.handleChangeColor(e)
        },
        handleChangeColor(e) {
            if (
                e.changedTouches[0].clientY < this.areaPos.top ||
                e.changedTouches[0].clientY > this.areaPos.top + this.areaSize ||
                e.changedTouches[0].clientX < this.areaPos.left ||
                e.changedTouches[0].clientX > this.areaPos.left + this.areaSize
            ) {
                return
            }
            this.dotTop = e.changedTouches[0].clientY - this.areaPos.top - 5
            this.dotLeft = e.changedTouches[0].clientX - this.areaPos.left - 5
            let s = (this.dotLeft + 5) / this.areaSize
            let l = (this.areaSize - this.dotTop + 5) / this.areaSize
            let hsv = rgb2hsv(this.rgb.r, this.rgb.g, this.rgb.b)
            let rgb = hsv2rgb(hsv.h, s, l)
            this.value111 = rgb2Hex(rgb.r, rgb.g, rgb.b).toUpperCase()
        },
        reset(value) {
            if (this.value1 !== value) {
                this.value1 = value
                this.$emit('change', { detail: { value: this.value1 } })
                this.$emit('input', this.value1)
            }
        }
    },
    created() {
        this.init()
    }
}
</script>

<style lang="scss" scoped>
.color-selector {
    position: relative;
    .solid-red {
        &::before {
            width: auto;
            height: auto;
            top: -6rpx;
            right: -6rpx;
            bottom: -6rpx;
            left: -6rpx;
        }
    }
    .color-area {
        position: relative;
        overflow: hidden;
        &::before {
            content: '';
            position: absolute;
            z-index: 1;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            background-image: linear-gradient(left, rgba(255, 255, 255, 1), rgba(0, 0, 0, 0));
        }
        &::after {
            content: '';
            position: absolute;
            z-index: 2;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            background-image: linear-gradient(bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
        }
        .dot {
            position: absolute;
            z-index: 3;
        }
    }
    .hue-area {
        background-image: linear-gradient(
            top,
            #ff0000 0%,
            #ffff00 17%,
            #00ff00 33%,
            #00ffff 50%,
            #0000ff 67%,
            #ff00ff 83%,
            #ff0000 100%
        );
        position: relative;
        .btn {
            position: absolute;
        }
    }
    .preview {
        width: 100rpx;
        height: 100rpx;
    }
}
</style>
